package com.leedsoft.common.activiti;

import com.leedsoft.common.Enums;
import com.leedsoft.domain.AidBpmNode;
import com.leedsoft.domain.AidBpmNodeExample;
import com.leedsoft.domain.AidBpmSeqflow;
import com.leedsoft.domain.AidBpmSeqflowExample;
import com.leedsoft.mapper.AidBpmNodeMapper;
import com.leedsoft.mapper.AidBpmSeqflowMapper;
import org.activiti.bpmn.BpmnAutoLayout;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.List;

/**
 * Created by leeds on 2017/6/9.
 */
@Component
public class ActivitiHelper {
    private static final String START_EVENT_ID = "_0";
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ProcessEngine engine;
    @Autowired
    private AidBpmNodeMapper nodeMapper;
    @Autowired
    private AidBpmSeqflowMapper flowMapper;

    /**
     *  解析保存的流程定义数据，动态生成流程定义
     * @param planId
     * @return
     */
    public String genProcDef(Long planId) {
        String procDefId;
        try {
            BpmnModel model = new BpmnModel();
            Process process = buildProcess(planId);//planId
            model.addProcess(process);
            // 2. Generate graphical information
            new BpmnAutoLayout(model).execute();
            // 3. Deploy the process to the engine
            Deployment deployment = engine.getRepositoryService().createDeployment()
                    .addBpmnModel(process.getId() + ".bpmn", model)
                    .name(process.getId() + "_deployment").deploy();
            procDefId = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId())
                    .singleResult().getId();
        } catch (Exception e) {
            throw new RuntimeException("动态生成流程定义失败！", e);
        }
        return procDefId;
    }


    private Process buildProcess(Long planId) {
        Process process = new Process();
        process.setId("procdef_" + planId);//process key
        process.addFlowElement(createStartEvent());

        AidBpmNodeExample nodeExample = new AidBpmNodeExample();
        nodeExample.createCriteria().andPlan_idEqualTo(planId);
        List<AidBpmNode> nodes = nodeMapper.selectByExample(nodeExample);
        for (AidBpmNode node : nodes) {
            switch ((Enums.NodeType)Enums.getEnum("NodeType", node.getType().intValue())) {
                case TASK:
                    process.addFlowElement(createUserTask(node.getCode(),
                            node.getBrief(), node.getAssignee()));
                    break;
                case EXCLUSIVE_GATEWAY:
                    process.addFlowElement(createExGateway(node.getCode()));
                    break;
                case INCLUSIVE_GATEWAY:
                    process.addFlowElement(createInGateway(node.getCode()));
                    break;
                case OVER:
                    process.addFlowElement(createEndEvent(node.getCode()));
                    break;
            }
        }

        AidBpmSeqflowExample flowExample = new AidBpmSeqflowExample();
        flowExample.createCriteria().andPlan_idEqualTo(planId);
        List<AidBpmSeqflow> flows = flowMapper.selectByExample(flowExample);
        for (AidBpmSeqflow flow : flows) {
            process.addFlowElement(createSequenceFlow(flow.getFrom(), flow.getTo(),
                    flow.getLabel(), flow.getExp()));
        }
        return process;
    }

    /*任务节点*/
    private static UserTask createUserTask(Integer id, String name, Long assignee) {
        UserTask userTask = new UserTask();
        userTask.setId(codeToStr(id));
        userTask.setName(name);
        userTask.setAssignee(assignee.toString());
        return userTask;
    }

    /*连线*/
    private static SequenceFlow createSequenceFlow(Integer from, Integer to, String label, String exp) {
        SequenceFlow flow = new SequenceFlow();
        flow.setSourceRef(codeToStr(from));
        flow.setTargetRef(codeToStr(to));
        flow.setName(label);
        if (StringUtils.isNotEmpty(exp)) {
            flow.setConditionExpression(exp);
        }
        return flow;
    }

    /*排他网关*/
    private static ExclusiveGateway createExGateway(Integer id) {
        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
        exclusiveGateway.setId(codeToStr(id));
        return exclusiveGateway;
    }

    /*并发网关*/
    private static InclusiveGateway createInGateway(Integer id) {
        InclusiveGateway inclusiveGateway = new InclusiveGateway();
        inclusiveGateway.setId(codeToStr(id));
        return inclusiveGateway;
    }

    /*开始节点*/
    private static StartEvent createStartEvent() {
        StartEvent startEvent = new StartEvent();
        startEvent.setId(START_EVENT_ID);
        return startEvent;
    }

    /*结束节点*/
    private static EndEvent createEndEvent(Integer id) {
        EndEvent endEvent = new EndEvent();
        endEvent.setId(codeToStr(id));
        return endEvent;
    }

    private static String codeToStr(Integer id) {
        return "_" + id;
    }
}
